package com.yanqu.road.dao.db;

import com.yanqu.road.utils.MyProperties;
import com.yanqu.road.utils.StackHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;

/**
 * 数据库连接池管理类
 *
 * @功能 ：管理类DBConnectionManager支持对一个或多个由属性文件定义的数据库连接池的访问.
 * 客户程序可以调用getInstance()方法访问本类的唯一实例
 */
public class DBPoolManager {
    private static final Logger log = LogManager.getLogger(DBPoolManager.class.getName());
    private static DBPoolManager instance; // 唯一实例
    private Hashtable<String, BoneCPPool> pools = new Hashtable<String, BoneCPPool>();// 多种连接池
    private static MyProperties dbProps;// 属性文件
    private String dbPath;

    /**
     * 单例模式建构私有函数以防止其它对象创建本类实例
     */
    private DBPoolManager(String dbPath) {
        this.init(dbPath);
    }

    /**
     * 得到DBPool连接信息
     */
    public static MyProperties getPropertiesFile() {
        return dbProps;
    }

    /**
     * 采用单例模式，返回唯一实例.如果是第一次调用此方法,则创建实例
     */
    public static synchronized DBPoolManager getInstance(String dbPath) {
        if (instance == null) {
            instance = new DBPoolManager(dbPath);
        }
        return instance;
    }

    /**
     * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数 限制,则创建并返回新连接
     *
     * @param name 在属性文件中定义的连接池名字
     * @return Connection 可用连接或null
     */
    public Connection getConnection(String name) {
        BoneCPPool dbPool = pools.get(name);
        if (dbPool != null) {
            //获取JDBC连接池"
            return dbPool.getDbConnection();
        }
        log.error(String.format("no find db link pool,name:%s", name));
        return null;
    }

    /**
     * 关闭所有连接,撤销驱动程序的注册
     */
    public synchronized void closeFallow() {
        Enumeration<BoneCPPool> allPools = pools.elements();
        while (allPools.hasMoreElements()) {
            BoneCPPool pool = allPools.nextElement();
            pool.shutDownBoneCpDbPoll();
        }
    }

    public void addPools(Properties properties) {
        createPools(properties);
    }

    /**
     * 读取属性完成初始化
     */
    private void init(String dbPath) {
        this.dbPath = dbPath;
        dbProps = new MyProperties(dbPath);
        //创建连接池
        createPools(dbProps.getProperties());
    }

    /**
     * 根据指定属性创建连接池实例.
     *
     * @param props 连接池属性
     */
    private void createPools(Properties props) {
        Enumeration<?> propNames = props.propertyNames();
        while (propNames.hasMoreElements()) {
            String name = (String) propNames.nextElement();
            if (name.endsWith(".url")) {
                String poolName = name.substring(0, name.lastIndexOf("."));
                String config = decrypt(props.getProperty(poolName + ".url"));
                if (config == null || config.isEmpty())
                    continue;

                String[] result = config.split("\\|");
                String url = result[0];
                String user = result[1];
                String password = result[2];
                String maxConn = result[3];

                int max;
                try {
                    max = Integer.valueOf(maxConn).intValue();
                } catch (NumberFormatException e) {
                    log.error("最大连接数配置格式错误 ", e);
                    max = 0;
                }
                if (pools.contains(poolName)) {
                    return;
                }
                BoneCPPool pool = new BoneCPPool(poolName, url, user, password, max, 1, 2, 1);
                pools.put(poolName, pool);
                log.info("加载数据库连接池 :{},URL:{}完成", poolName, url);
            }
        }
    }

    /**
     * 解密数据库连接字符串
     *
     * @param str 数据库连接解密key，一般不会变动，写在程序中
     */
    private String decrypt(String str) {
        return str;
    }

    /**
     * 加密数据库连接字符串格式：url|user|password|maxconn
     *
     * @param str 数据库连接加密key，一般不会变动，写在程序中
     * @return
     */
    private String encrypt(String str) {
        return str;
    }

    public static void main(String[] args) {

    }
}
